package com.univocity.trader.strategy;

import com.univocity.trader.account.*;
import com.univocity.trader.candles.*;
import com.univocity.trader.indicators.*;

/**
 * 负责监控由 {@link Trader} 执行的交易，在 {@link Strategy} 返回一个 {@link Signal} 之后。
 * 它可能包含 {@link Indicator} 以帮助决定在从 {@link com.univocity.trader.Exchange} 接收到的每个滴答时应该做什么。
 * 一旦收到一个 {@code BUY}，并且没有打开的头寸，将调用 {@link #discardBuy(Strategy)} 来确认是否是买入的好时机。
 * 如果时机合适，将打开一个头寸，并调用 {@link #bought(Trade, Order)}。在那之后，将会在每次从交易所接收到的滴答后调用 {@link #handleStop(Trade)}，
 * 以允许关闭 {@link Trade}。
 * <p>
 * 如果 {@link #handleStop(Trade)} 返回一个非空值或从 {@link Strategy} 接收到一个 {@code SELL} 信号，
 * 将调用 {@link #allowExit(Trade)} 来确认是否退出交易。如果交易关闭，将调用 {@link #sold(Trade, Order)}。
 * 在交易的生命周期中，可能会调用其他几个方法：
 * <p>
 * - 当一个盈利的交易达到一个新的高正收益率时，调用 {@link #highestProfit(Trade, double)}。
 * - 当一个亏损的交易达到一个新的低负收益率时，调用 {@link #worstLoss(Trade, double)}。
 * - 当没有资金可用于交易，而一个 {@link Strategy} 发现一个买入机会时，将调用 {@link #allowTradeSwitch(Trade, String, Candle, String)}，
 * 询问当前交易是否可以退出以释放资金并在另一个工具上开立另一个交易。可以从 {@link Trader} 实例中获取有关交易的特定详细信息。
 *
 * @see Trader
 * @see IndicatorGroup
 * @see Strategy
 */
public abstract class StrategyMonitor extends IndicatorGroup {

    /**
     * 当前符号的交易状态的相关上下文信息。
     */
    protected Context context;

    /**
     * 负责创建和管理交易的对象。
     */
    protected Trader trader;

    /**
     * 测试当前交易状态，以确定是否应该退出交易。将退出交易，而不考虑由 {@link Strategy} 发出的任何 {@link Signal}。
     *
     * @param trade 控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @return 如果交易应该保持开放，则返回 {@code null}；如果交易应该退出，则返回一个带有退出原因的 {@code String}。
     * 该消息将然后由 {@link Trade#exitReason()} 返回，并可以包含在日志或电子邮件中（如在 {@link com.univocity.trader.notification.OrderExecutionToEmail} 中实现）。
     */
    public String handleStop(Trade trade) {
        return null;
    }

    /**
     * 检查由给定 {@link Strategy} 发出的 {@code BUY} 信号是否应该被丢弃，以防止 {@link Trader} 买入。
     *
     * @param strategy 发出 {@code BUY} 信号的策略。
     * @return 如果 {@link #trader} 被允许买入其负责的仪器，则返回 {@code false}；如果应该忽略 {@code BUY} 信号，则返回 {@code true}。
     */
    public boolean discardBuy(Strategy strategy) {
        return false;
    }

    /**
     * 检查由给定 {@link Strategy} 发出的 {@code SELL} 信号是否应该被丢弃，以防止 {@link Trader} 做空。
     *
     * @param strategy 发出 {@code SELL} 信号的策略。
     * @return 如果 {@link #trader} 被允许使用其负责的仪器开立一个空头交易，则返回 {@code false}；如果应该忽略 {@code SELL} 信号，则返回 {@code true}。
     */
    public boolean discardShortSell(Strategy strategy) {
        return false;
    }

    /**
     * 指示此监控器是否允许来自多个策略的信号，即如果正在使用多个 {@link Strategy}，一个可以发出 {@code BUY} 信号，另一个可以发出 {@code SELL} 信号，
     * 并且两者都将被接受。如果不允许，一旦一个 {@link Strategy} 进入一个交易，由另一个 {@link Strategy} 发出的信号将被忽略。
     *
     * @return 如果开启一个交易的 {@link Strategy} 必须关闭它，否则返回 {@code true}。
     */
    public boolean allowMixedStrategies() {
        return true;
    }

    /**
     * 通知最新的价格变动达到了自给定 {@link Trade} 中第一个 {@link Order} 打开以来的最高正点。
     *
     * @param trade  控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @param change 当前交易的正值收益率（作为一个大于0.0的百分比值，并在100的范围内）。
     */
    public void highestProfit(Trade trade, double change) {
    }

    /**
     * 通知最新的价格变动达到了自给定 {@link Trade} 中第一个 {@link Order} 打开以来的最低负点。
     *
     * @param trade  控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @param change 当前交易的负值收益率（作为一个在-100.0和0.0之间的百分比值）。
     */
    public void worstLoss(Trade trade, double change) {
    }

    /**
     * 通知 {@link #trader} 打开了买入一定数量符号 {@link Trade#symbol()} 的 {@link Order}。
     *
     * @param trade 控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @param order 添加到给定交易中的 {@code BUY} 订单，并通过 {@link Trade#position()} 可用。
     */
    public void bought(Trade trade, Order order) {
    }

    /**
     * 通知 {@link #trader} 打开了卖出一定数量符号 {@link Trade#symbol()} 的 {@link Order}。
     *
     * @param trade 控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @param order 添加到给定交易中的 {@code SELL} 订单，并通过 {@link Trade#exitOrders()} 可用。
     */
    public void sold(Trade trade, Order order) {
    }

    /**
     * 确认给定的交易可以关闭。
     *
     * @param trade 控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @return 如果交易可以退出，并且可以生成一个 {@code SELL} 订单来出售账户中持有的所有资产，则返回 {@code true}，否则返回 {@code false}。
     */
    public boolean allowExit(Trade trade) {
        return true;
    }

    /**
     * 检查当前打开的头寸是否可以关闭，以释放资金用于在另一个工具上开立另一个交易。
     *
     * @param trade        控制 {@link #trader} 持有的头寸状态的一组多个订单。
     * @param exitSymbol   如果当前打开的交易可以关闭，则用于购买的证券的符号（例如 BTC, USD）。
     * @param candle       退出符号的最新蜡烛。
     * @param candleTicker 给定蜡烛的完整标记（例如 BTCETH, EURUSD）。
     * @return 一个标志，指示当前交易是否可以退出，并将头寸重新分配给给定的退出符号。
     */
    public boolean allowTradeSwitch(Trade trade, String exitSymbol, Candle candle, String candleTicker) {
        return false;
    }

    /**
     * 将与给定符号上的交易相关的上下文分配给此监控器。
     *
     * @param context 负责一个工具的所有交易信息的对象。
     */
    public void setContext(Context context) {
        if (this.context != null) {
            throw new IllegalStateException("不能在设置上下文后修改上下文");
        }
        this.context = context;
        this.trader = context.trader();
    }
}